package wili.logic;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import wili.c.Ca;
import wili.c.Str9a;

/**
 * Read multiLine logic expr。
 */
public class ToLineLogicNode {
	static int GenID;

	final Cont21 cont;

	public int lineNo = -1;

	public final int id = ++GenID;
	{
		if (id == 4)
			Ca.pause();
	}

	final ToLineLogicNode dad;

	public final String line;

	public List<ToLineLogicNode> sons;

	Boolean isAnd;

	boolean isYes = true;

	public String toString() {
		if (isYes)
			return line;
		return "!(" + line + ")";
	}

	public ToLineLogicNode(String line, ToLineLogicNode dad, Cont21 cont) {
		this.cont = cont;
		this.line = line;
		this.dad = dad;
	}

	public ToLineLogicNode(boolean isAnd, ToLineLogicNode dad, Cont21 cont) {
		this.cont = cont;
		this.line = isAnd ? "and" : "or";
		this.isAnd = isAnd;
		this.dad = dad;
	}

	/**
	 * @return 表达式的单行串形式
	 */
	public String toLineExpString() {
		StringBuilder sb = new StringBuilder();
		toLineExpString00(sb);
		return sb.toString();
	}

	void toLineExpString00(StringBuilder sb) {
		if (this.isAnd != null) {
			if (this.isAnd && this.isYes) {
				Ca.pause();
			} else {
				if (this.isYes) {
					sb.append("(");
				} else {
					sb.append("!(");
				}
				if (Cont21.DEBUG)
					sb.append(id);
			}
			for (int i = 0; i < sons.size(); i++) {
				if (i > 0) {
					sb.append(" ");
					sb.append(isAnd ? cont.andOp : cont.orOp);
					sb.append(" ");
				}
				ToLineLogicNode son = sons.get(i);
				son.toLineExpString00(sb);
			}
			if (this.isAnd && this.isYes) {
				Ca.pause();
			} else {
				if (Cont21.DEBUG)
					sb.append(id);
				sb.append(")");
			}
		} else {
			Ca.asert(sons == null, "id=" + id);
			sb.append(this.line);
		}
	}

	public ToLineLogicNode son(int ind) {
		if (ind < 0)
			ind = sons.size() + ind;
		return sons.get(ind);
	}

	public ToLineLogicNode nega() {
		this.isYes = !this.isYes;
		return this;
	}

	public ToLineLogicNode addSon(String st) {
		if (sons == null)
			sons = new ArrayList<>();
		ToLineLogicNode son;
		sons.add(son = new ToLineLogicNode(st, this, cont));
		return son;
	}

	public ToLineLogicNode addSon(boolean isAnd) {
		if (this.sons == null)
			sons = new ArrayList<>();
		ToLineLogicNode son;
		sons.add(son = new ToLineLogicNode(isAnd, this, cont));
		return son;
	}

	/**
	 * 读入多行表达，生成结点树。
	 */
	public static ToLineLogicNode newInst(String[] lines, Cont21 cont) {
		if (cont == null)
			cont = new Cont21("and", "or");
		int indent = -1;
		int[] dents = new int[lines.length];
		for (int i = 0; i < lines.length; i++) {
			String line = lines[i] = lines[i].replaceFirst("\\s+$", "");
			String st = line.trim();
			if (st.isEmpty() || st.startsWith("//") || st.startsWith("===")) {
				dents[i] = -1;
			}
			String dentSt = Str9a.readPat("[\\s]+", line, 0);
			dents[i] = dentSt.length();
			if (indent == -1 && dents[i] > 0) {
				indent = dents[i];//第1个缩进的长度为单位缩进
			}
		}
		Stack<ToLineLogicNode> stack = new Stack<>();
		int preDent = 0;
		ToLineLogicNode root = new ToLineLogicNode("ROOT", null, cont), dad = root;
		for (int i = 0; i < lines.length; i++) {
			if (dents[i] == -1)
				continue;
			String line = lines[i];
			String st = line.trim();
			if (st.isEmpty() || st.startsWith("//") || st.startsWith("===")) {
				continue;
			}
			ToLineLogicNode lineNode;
			st = lines[i] + ", i=" + i;
			Ca.log(-1, st);
			line = line.trim();
			int dent = dents[i];
			{
				if (dent > preDent) {//
					dad = stack.peek();//上行更浅，取出父
				} else if (dent < preDent) {
					if (stack.isEmpty())
						Ca.pause();
					int indentNum = (int) Math.round((preDent - dent) / (double) indent);
					if ((preDent - dent) % indent != 0) {
						Ca.log(1, "\nWarn: indent at line " + i + " or " + (i - 1) + " is not normal: "
								+ lines[i - 1].trim());
					}
					if (indentNum > 1)
						Ca.pause();
					for (int j = 0; j < indentNum; j++) {
						dad = stack.pop();//上行更深，弹出父
						dad = stack.peek();//取得上行的爷为本行的父
					}
				}
				{
					if ("and".equals(line)) {
						lineNode = dad.addSon(true);
					} else if ("or".equals(line)) {
						lineNode = dad.addSon(false);
					} else if ("andNot".equals(line)) {
						lineNode = dad.addSon(true).nega();
					} else if ("orNot".equals(line)) {
						lineNode = dad.addSon(false).nega();
					} else {
						lineNode = dad.addSon(line);
					}
					lineNode.lineNo = i + 1;
					if (root.dad == null) {
						root = lineNode;
					}
				}
				if (i + 1 < lines.length) {
					if (dent < dents[i + 1]) {//下行更深，本父入栈
						stack.push(dad = lineNode);
					} else if (dent > dents[i + 1]) {
						//下行更浅
					}
				}
				preDent = dent;
				continue;
			}
		}
		Ca.asert(root.dad.dad == null, dad);
		Ca.asert(root.dad.sons.size() == 1, root.dad.sons.size());
		return root;
	}
}